package org.sky.logger.aop;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.sky.logger.api.LogIgnore;
import org.sky.logger.api.LogMethod;
import org.sky.logger.json.JsonSerializer;
import org.sky.logger.json.JsonSerializerLoader;
import org.sky.logger.mdc.LogMdc;
import org.sky.logger.util.AopUtil;
import org.sky.logger.util.Constant;
import org.sky.logger.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 方法日志切面
 * 
 * @author gaofu
 *
 */
public class LogAspect {
	private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
	private JsonSerializer jsonSerializer = JsonSerializerLoader.loadJsonSerializer();

	/**
	 * 单条日志最大长度
	 */
	private int maxLength = 10000;

	public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
		Object result = null;
		Method pointMethod = null;
		LogMethod methodLog = null;
		boolean isPointcut = false;
		// 前置通知
		try {
			pointMethod = AopUtil.pointMethod(pjp);
			methodLog = AopUtil.annotation(pointMethod, LogMethod.class);
			if (methodLog != null) {
				String pointcut = LogMdc.get(Constant.Pointcut);
				if (Util.isEmpty(pointcut)) {
					String logid = Util.logId();
					pointcut = classMethod(pointMethod);
					LogMdc.init(logid, pointcut);
				} else {
					isPointcut = true;
				}
				beginLog(pointMethod, pjp, methodLog.logParam());
			}
		} catch (Throwable t) {
			logger.error(pointMethod.getName() + "方法开始日志异常", t);
		}
		try {
			// 执行目标方法
			result = pjp.proceed();
		} finally {
			// 后置通知
			try {
				if (methodLog != null) {
					finishLog(pointMethod, result, methodLog.logReturn());
					if (!isPointcut) {
						LogMdc.clear();
					}
				}
			} catch (Throwable t) {
				logger.error(pointMethod.getName() + "方法结束日志异常", t);
			}
		}
		return result;
	}

	protected String classMethod(Method pointMethod) {
		StringBuilder pointcut = new StringBuilder();
		pointcut.append(pointMethod.getDeclaringClass().getSimpleName()).append(".").append(pointMethod.getName());
		return pointcut.toString();
	}

	/**
	 * 打印进入方法日志
	 * 
	 */
	protected void beginLog(Method pointMethod, ProceedingJoinPoint pjp, boolean logParam) {
		StringBuilder log = new StringBuilder();
		log.append(classMethod(pointMethod)).append(" begin ");
		//
		if (logParam) {
			log.append(" ==>  ");
			try {
				if (jsonSerializer != null) {
					Map<String, Object> arglist = new HashMap<String, Object>();
					// 参数值
					Object[] paramValues = pjp.getArgs();
					// 参数名称
					Class<?>[] paramNames = pointMethod.getParameterTypes();
					// 参数注解
					Annotation[][] paramAnnotations = pointMethod.getParameterAnnotations();
					for (int i = 0; i < paramAnnotations.length; i++) {
						Class<?> paramName = paramNames[i];
						Object paramValue = paramValues[i];
						boolean logIgnore = false;
						if (paramValue != null) {
							Annotation[] paramAnnotation = paramAnnotations[i];
							logIgnore = logIgnore(paramValue);
							if (!logIgnore) {
								LogIgnore _logIgnore = AopUtil.annotation(paramAnnotation, LogIgnore.class);
								if (_logIgnore != null) {
									logIgnore = true;
								}
							}
						}
						if (!logIgnore) {
							arglist.put(paramName.getSimpleName(), paramValue);
						}
					}
					if (arglist.isEmpty()) {
						log.append("null");
					} else {
						String json = jsonSerializer.toJson(arglist);
						log.append(maxLog(json));
					}
				} else {
					log.append("null");
				}
			} catch (Throwable e) {
				logger.error("读取方法参数异常", e);
				log.append("ERROR");
			}
		}
		logger.info(log.toString());
	}

	/**
	 * 打印离开方法日志
	 * 
	 */
	protected void finishLog(Method pointMethod, Object obj, boolean logReturn) {
		StringBuilder log = new StringBuilder();
		log.append(classMethod(pointMethod)).append(" end ");
		if (logReturn) {
			log.append(" <==  ");
			try {
				if (jsonSerializer != null) {
					if (obj == null || logIgnore(obj)) {
						log.append("null");
					} else {
						String json = jsonSerializer.toJson(obj);
						log.append(maxLog(json));
					}
				} else {
					log.append("null");
				}
			} catch (Throwable e) {
				logger.error("读取方法结果异常", e);
				log.append("ERROR");
			}
		}
		logger.info(log.toString());
	}

	/**
	 * 单条日志最大长度
	 * 
	 * @param log
	 * @return
	 */
	protected String maxLog(String log) {
		int max = getMaxLength();
		if (log != null && log.length() > max) {
			log = log.substring(0, max) + "...";
		}
		return log;
	}

	/**
	 * 是否打印日志
	 * 
	 * @param obj
	 * @return
	 */
	protected boolean logIgnore(Object obj) {
		boolean logIgnore = obj instanceof InputStream || obj instanceof OutputStream;
		return logIgnore;
	}

	public int getMaxLength() {
		return maxLength;
	}

	public void setMaxLength(int maxLength) {
		this.maxLength = maxLength;
	}

	public JsonSerializer getJsonSerializer() {
		return jsonSerializer;
	}

	public void setJsonSerializer(JsonSerializer jsonSerializer) {
		this.jsonSerializer = jsonSerializer;
	}

}
